﻿using System.Diagnostics;

namespace UPKAndUDKUnpackTools.Tools;

/// <summary>
///     负责从 UPK 文件中导出音频和模型的类。
/// </summary>
public static class UpkExporter
{
    private static int _failedFileCount;
    private static readonly List<string> FailedFilesList = [];
    private static int _successFileCount;
    private static readonly ParallelOptions Options;
    private static int pv;

    static UpkExporter()
    {
        Options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
    }

    public static event Action<int> pValueOnUpdated;

    /// <summary>
    ///     导出 UPK 文件中的音频。
    /// </summary>
    /// <param name="directory">UPK 文件所在目录。</param>
    /// <param name="tempOutputDir">临时输出目录。</param>
    /// <param name="finalOutputDir">最终输出目录。</param>
    public static void ExportSoundForUpkFiles(string directory, string tempOutputDir, string finalOutputDir)
    {
        var files = Directory.EnumerateFiles(directory, "*.upk", SearchOption.AllDirectories);

        foreach (var filePath in files)
        {
            pv++;
            Console.WriteLine($"正在处理文件: {GetSubstringFromRight(filePath)}");
            ExportSoundsFile(filePath, tempOutputDir);
            MoveFsbFiles(tempOutputDir, finalOutputDir);
            pValueOnUpdated?.Invoke(pv);
        }
    }


    /// <summary>
    ///     导出 UPK 文件中的模型。
    /// </summary>
    /// <param name="directory">UPK 文件所在目录。</param>
    /// <param name="finalOutputDir">最终输出目录。</param>
    public static void ExportModelsAndAnimsForUpkFiles(string directory, string finalOutputDir)
    {
        var files = Directory.EnumerateFiles(directory, "*.upk", SearchOption.AllDirectories);
        foreach (var filePath in files)
        {
            pv++;
            Console.WriteLine($"正在处理文件: {GetSubstringFromRight(filePath)}");
            ExportModelFile(filePath, finalOutputDir);
            pValueOnUpdated?.Invoke(pv);
        }
    }


    /// <summary>
    ///     导出指定文件的音频。
    /// </summary>
    /// <param name="filePath">UPK 文件路径。</param>
    /// <param name="outputDir">输出目录。</param>
    private static void ExportSoundsFile(string filePath, string outputDir)
    {
        try
        {
            var uModels = new ProcessStartInfo
            {
                FileName = "umodel64",
                Arguments = $"-export -game=gunsl -sounds -out=\"{outputDir}\" \"{filePath}\"",
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };
            using var process = Process.Start(uModels);
            if (process == null) return;
            Console.ForegroundColor = ConsoleColor.DarkCyan;
            process.OutputDataReceived += (_, args) => Console.WriteLine(args.Data);
            process.ErrorDataReceived += (_, args) => Console.WriteLine(args.Data);
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
            var exited = process.WaitForExit(60000);
            Console.ResetColor();
            if (!exited)
            {
                process.Kill();
                Console.WriteLine("进程超时被终止");
            }
            else
            {
                if (process.ExitCode == 0)
                {
                    _successFileCount++;
                    Console.WriteLine($"成功导出: {GetSubstringFromRight(filePath)}");
                }
                else
                {
                    _failedFileCount++;
                    Console.WriteLine($"导出失败: {GetSubstringFromRight(filePath)}");
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine($"导出 {GetSubstringFromRight(filePath)} 发生错误: {e.Message}");
        }
    }

    /// <summary>
    ///     导出指定文件的模型。
    /// </summary>
    /// <param name="filePath">UPK 文件路径。</param>
    /// <param name="outputDir">输出目录。</param>
    private static void ExportModelFile(string filePath, string outputDir)
    {
        var lightmap = Form1.noLighting ? "-nolightmap" : "";
        try
        {
            var uModels = new ProcessStartInfo
            {
                FileName = "umodel64",
                Arguments = $"-export -game=gunsl {lightmap} -lods -nooverwrite -out=\"{outputDir}\" \"{filePath}\"",
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };
            using var process = Process.Start(uModels);
            if (process == null) return;
            Console.ForegroundColor = ConsoleColor.DarkCyan;
            process.OutputDataReceived += (_, args) => Console.WriteLine(args.Data);
            process.ErrorDataReceived += (_, args) => Console.WriteLine(args.Data);
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
            var exited = process.WaitForExit(60000);
            Console.ResetColor();
            if (!exited)
            {
                process.Kill();
                Console.WriteLine("进程超时被终止");
            }
            else
            {
                //Console.WriteLine(process.ExitCode == 0 ? $"成功导出: {filePath}" : $"导出失败: {filePath}");
                if (process.ExitCode == 0)
                {
                    _successFileCount++;
                    Console.WriteLine($"成功导出: {GetSubstringFromRight(filePath)}");
                }
                else
                {
                    _failedFileCount++;
                    Console.WriteLine($"导出失败: {GetSubstringFromRight(filePath)}");
                    FailedFilesList.Add(filePath);
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine($"导出 {GetSubstringFromRight(filePath)} 发生错误: {e.Message}");
        }
    }

    private static void MoveFsbFiles(string tempOutputDir, string finalOutputDir)
    {
        var files = Directory.EnumerateFiles(tempOutputDir, "*.fsb", SearchOption.AllDirectories);
        Parallel.ForEach(files, Options, sourcePath =>
        {
            try
            {
                if (File.Exists(sourcePath))
                {
                    var fileName = Path.GetFileName(sourcePath);
                    var fileNameParts = Path.GetFileNameWithoutExtension(fileName).Split('_');
                    string specificOutputDir;

                    switch (fileNameParts.Length)
                    {
                        case > 2 when fileNameParts[1].Equals("f", StringComparison.OrdinalIgnoreCase):
                            specificOutputDir = Path.Combine(finalOutputDir, fileNameParts[2]);
                            break;
                        case > 1:
                        {
                            var primaryFolder = fileNameParts[0];
                            var secondaryFolder = fileNameParts[1];
                            specificOutputDir = Path.Combine(finalOutputDir, primaryFolder, secondaryFolder);
                            break;
                        }
                        default:
                            specificOutputDir = Path.Combine(finalOutputDir, "unknown");
                            break;
                    }

                    Directory.CreateDirectory(specificOutputDir);
                    var destinationPath = Path.Combine(specificOutputDir, fileName);

                    var count = 1;
                    var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
                    var extension = Path.GetExtension(fileName);

                    while (File.Exists(destinationPath))
                    {
                        var newFileName = $"{fileNameWithoutExtension}_{count}{extension}";
                        destinationPath = Path.Combine(specificOutputDir, newFileName);
                        count++;
                    }

                    File.Move(sourcePath, destinationPath);
                    Console.WriteLine($"移动 FSB 文件: {GetSubstringFromRight(sourcePath)} 到 {destinationPath}");
                }
                else
                {
                    Console.WriteLine($"文件未找到: {GetSubstringFromRight(sourcePath)}");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"移动文件时出错: {GetSubstringFromRight(sourcePath)} - 错误: {ex.Message}");
            }
        });
    }


    public static void GetStatus(string type)
    {
        MessageBox.Show($"已成功解锁导出 {_successFileCount} 个 upk 文件，有 {_failedFileCount} 个导出失败。", "导出完成",
            MessageBoxButtons.OK, MessageBoxIcon.Information);
        // 如果有文件导出失败，则输出失败的文件列表并进行二次处理
        if (_failedFileCount != 0)
        {
            Console.WriteLine("============失败 upk 文件列表============");
            foreach (var filePath in FailedFilesList) Console.WriteLine(filePath);
            Console.WriteLine();
            Console.WriteLine("以上文件导出失败，即将进行第二次处理。。。。。");
            // 并行处理失败的文件，以提高效率
            Parallel.ForEach(FailedFilesList, UpkProcessor.ErrFileHandle);

            string path;
            // 根据传入的类型决定后续处理时的目标文件夹
            switch (type)
            {
                case "2":
                    path = DirectoryManager.CreateFolder("Models");
                    ExportModelsAndAnimsForUpkFiles("unlockedUpk", path);
                    break;
                case "3":
                    path = DirectoryManager.CreateFolder("Animations");
                    ExportModelsAndAnimsForUpkFiles("unlockedUpk", path);
                    break;
            }
        }

        // 重置计数器，以便后续使用
        _successFileCount = 0;
        _failedFileCount = 0;
        pv = 0;
    }

    public static string GetSubstringFromRight(string input)
    {
        if (string.IsNullOrEmpty(input)) return string.Empty;
        // 找到最后一个 '/' 或 '\' 的位置
        var lastIndex = input.LastIndexOfAny(['/', '\\']);

        // 如果没有找到，返回整个字符串
        return lastIndex == -1
            ? input
            :
            // 从最后一个 '/' 或 '\' 之后开始取子字符串
            input[(lastIndex + 1)..];
    }
}